/*=====================================================================
  This file is part of a Microsoft SQL Server Shared Source Application.
  Copyright (C) Microsoft Corporation.  All rights reserved.
 
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
======================================================= */

using System;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.SqlServer.BI.ASLoadSim
{
    /// <summary>
    /// Driver for AS Load Simulator.
    /// </summary>    
    [TestClass]
    public class Driver
    {
        #region use TestContext to access test properties

        private TestContext m_testContext;
        /// <summary>
        /// NOTE: This property is a must. And this property's name must be TestContext (case sensitive).
        /// The unit test adapter uses reflection to find a property named TestContext of type TestContext 
        /// and then sets it before running the test.
        /// </summary>
        public TestContext TestContext
        {
            get
            {
                return m_testContext;
            }
            set
            {
                m_testContext = value;
            }
        }

        #endregion

        private static int s_userId = 0;

        private CustomDataSource m_customDS;
        private ASAdoMdNetConnection m_conn;
        private int m_thinkTimeMin;
        private int m_thinkTimeMax;
        private string m_logToServer;
        private string m_logToDB;
        private int m_runDuration;
        private Dictionary<string, string> m_queryDictionary;

        public Driver()
        {        

        }

        [TestInitialize]
        public void LoadInitialize()
        {
            try
            {
                if (!m_testContext.Properties.Contains("Server"))
                    throw new Exception("Property \"Server\" is missing in the configuration file.");
                string server = (string)m_testContext.Properties["Server"];

                if (!m_testContext.Properties.Contains("Database"))
                    throw new Exception("Property \"Database\" is missing in the configuration file.");
                string database = (string)m_testContext.Properties["Database"];

                m_conn = new ASAdoMdNetConnection(server, database);

                m_conn.Connect(m_testContext);

                if (!m_testContext.Properties.Contains("ClientFileName"))
                    throw new Exception("Property \"ClientFileName\" is missing in the configuration file.");
                string fileName = (string)m_testContext.Properties["ClientFileName"];

                if (!m_testContext.Properties.Contains("ClientFileStartingNumber"))
                    throw new Exception("Property \"ClientFileStartingNumber\" is missing in the configuration file.");
                int startingNumber = Int32.Parse(m_testContext.Properties["ClientFileStartingNumber"].ToString());

                if (!m_testContext.Properties.Contains("NumOfClientFiles"))
                    throw new Exception("Property \"NumOfClientFiles\" is missing in the configuration file.");
                int numOfClientFiles = Int32.Parse(m_testContext.Properties["NumOfClientFiles"].ToString());

                int id = fileName.LastIndexOf('.');
                int userId = Interlocked.Increment(ref s_userId);
                int fileId = userId % numOfClientFiles + startingNumber;
                string clientFileName = fileName.Substring(0, id) + fileId.ToString() + fileName.Substring(id);

                if (!m_testContext.Properties.Contains("DataAccessMethod"))
                    throw new Exception("Property \"DataAccessMethod\" is missing in the configuration file.");
                string accessMethod = (string)m_testContext.Properties["DataAccessMethod"];

                m_customDS = new CustomDataSource(clientFileName, accessMethod);
                m_customDS.Initialize();

                if (!m_testContext.Properties.Contains("ThinkTimeMin"))
                    throw new Exception("Property \"ThinkTimeMin\" is missing in the configuration file.");
                m_thinkTimeMin = Int32.Parse(m_testContext.Properties["ThinkTimeMin"].ToString());

                if (!m_testContext.Properties.Contains("ThinkTimeMax"))
                    throw new Exception("Property \"ThinkTimeMax\" is missing in the configuration file.");
                m_thinkTimeMax = Int32.Parse(m_testContext.Properties["ThinkTimeMax"].ToString());

                if (!m_testContext.Properties.Contains("LogToServer"))
                    throw new Exception("Property \"LogToServer\" is missing in the configuration file.");
                m_logToServer = (string)m_testContext.Properties["LogToServer"];

                if (!m_testContext.Properties.Contains("LogToDB"))
                    throw new Exception("Property \"LogToDB\" is missing in the configuration file.");
                m_logToDB = (string)m_testContext.Properties["LogToDB"];

                if (!m_testContext.Properties.Contains("RunDuration"))
                    throw new Exception("Property \"RunDuration\" is missing in the configuration file.");
                m_runDuration = Int32.Parse(m_testContext.Properties["RunDuration"].ToString());

                m_queryDictionary = new Dictionary<string, string>();
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        [TestMethod]
        public void LoadStart()
        {
            try
            {
                DateTime methodStartTime = DateTime.Now;

                while (m_customDS.HasAvaiableQueries())
                {
                    TimeSpan timeRunning = DateTime.Now - methodStartTime;

                    // If there are less than 1 minute left before time expires, then break. 
                    // Otherwise, VSTS Load Test Tool will discard results of prior query execution. 
                    // (RunDuration is in the number of seconds.)
                    if (m_runDuration - timeRunning.TotalSeconds <= 60)
                    {
                        break;
                    }

                    // Get query to execute.
                    string query = m_customDS.RetrieveQuery();
                    // Comment part of the query.
                    string queryComment = query.Substring(0, query.IndexOf("*/") + 2);
                    // Add to query dictionary.
                    m_queryDictionary.Add(queryComment, query);

                    // Execute query.
                    m_conn.SendQuery(m_testContext, query, queryComment);

                    // Simulate think time.
                    Random rand = new Random();
                    int thinkTime = rand.Next(m_thinkTimeMin, m_thinkTimeMax);
                    Thread.Sleep(thinkTime * 1000);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        [TestCleanup]
        public void LoadCleanup()
        {
            try
            {
                // Now insert the entire query text to table [WebLoadTestTransactionContent]
                TransText.Insert(
                        m_logToServer,
                        m_logToDB,
                        "TTC",
                        "Time to Connect");

                Dictionary<string, string>.KeyCollection keys = m_queryDictionary.Keys;
                foreach (string key in keys)
                {
                    string value;
                    m_queryDictionary.TryGetValue(key, out value);

                    TransText.Insert(
                        m_logToServer,
                        m_logToDB,
                        "TTFB: " + key,
                        "TTFB: " + value);

                    TransText.Insert(
                        m_logToServer,
                        m_logToDB,
                        "TTLB: " + key,
                        "TTLB: " + value);
                }

                // Close connection
                m_conn.Disconnect();
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion
    }
}
